package com.warmheart.core.util.md5;

import java.security.MessageDigest;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.commons.lang3.StringUtils;

import com.warmheart.core.util.ObjUtil;

/**
 * 签名辅助类。 以对象进行签名时，对象需要包含"sign"字段。
 */
public class Md5SignUtil {

    private static final String SIGN = "sign";

    private static char[] HexCode = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
            'e', 'f' };

    private Md5SignUtil() {
    }

    /**
     * md5签名。 只针对简单object对象有效，即object中所有field都是基础类型。
     *
     * @param obj
     *            需要签名的对象
     * @param key
     *            签名秘钥
     * @return
     */
    public static String signMd5(Object obj, String key) {
        SortedMap<String, Object> map = ObjUtil.obj2TreemapExcludeEmpty(obj);
        return signMd5ByMap(map, key);
    }

    /**
     * @param params
     * @param key
     * @return
     */
    private static String signMd5ByMap(SortedMap<String, Object> params, String key) {
        if (null == params || params.isEmpty()) {
            return null;
        }
        String result = signMd5(params, key, null);
        return result;
    }

    public static String signMd5ByMapWithKeyStr(Object obj, String key, String keyStr) {
        SortedMap<String, Object> map = ObjUtil.obj2TreemapExcludeEmpty(obj);
        String result = signMd5ByMapWithKeyStr(map, key, keyStr);
        return result;
    }

    /**
     * @param params
     * @param key
     * @return
     */
    private static String signMd5ByMapWithKeyStr(SortedMap<String, Object> params, String key, String keyStr) {
        if (null == params || params.isEmpty()) {
            return null;
        }
        String result = signMd5(params, key, keyStr);
        return result;
    }

    /**
     * 校验当前对象的md5签名是否真确。 对象中的空字段不参与签名。
     *
     * @param obj
     * @param key
     * @return 签名正确返回true
     */
    public static boolean md5SignValidate(Object obj, String key) {
        if (null == obj) {
            throw new RuntimeException();
        }
        TreeMap<String, Object> map = ObjUtil.obj2TreemapExcludeEmpty(obj);
        String sign;
        if (map.containsKey(SIGN)) {
            sign = (String) map.get(SIGN);
            map.remove(SIGN);
        } else {
            throw new RuntimeException("sign field not found");
        }
        if (!StringUtils.equals(signMd5(map, key, null), sign)) {
            throw new RuntimeException();
        }
        return true;
    }

    private static String signMd5(SortedMap<String, Object> params, String key, String keyStr) {
        SortedMap<String, Object> temp = new TreeMap<String, Object>();
        Iterator<Entry<String, Object>> iterator = params.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = iterator.next();
            if (entry.getValue() != null && StringUtils.isNotBlank(entry.getValue().toString())) {
                temp.put(entry.getKey(), entry.getValue());
            }
        }
        if (null == keyStr) {
            keyStr = "&key=";
        } else if (!"".equals(keyStr)) {
            keyStr = "&" + keyStr + "=";
        }
        String source = toUrlParams(temp) + keyStr + key;
        System.out.println("===================================签名前字符串信息");
        System.out.println(source);
        System.out.println("===================================END");
        return md5(source).toUpperCase();
    }

    private static String toUrlParams(Map<String, Object> params) {
        StringBuilder sb = new StringBuilder();
        Iterator<String> iterator = params.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            sb.append(key + "=" + params.get(key).toString() + "&");
        }
        return sb.substring(0, sb.length() - 1);
    }

    private static String md5(String message) {
        try {
            MessageDigest md = MessageDigest.getInstance("md5");
            byte[] digest = md.digest(message.getBytes("UTF-8"));
            return toHex(digest);
        } catch (Exception e) {
            throw new RuntimeException(e.getCause());
        }
    }

    private static String toHex(byte[] data) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < data.length; ++i) {
            result.append(toHex(data[i]));
        }
        return result.toString();
    }

    private static String toHex(byte data) {
        StringBuilder buffer = new StringBuilder();
        buffer.append(HexCode[data >>> 4 & 15]);
        buffer.append(HexCode[data & 15]);
        return buffer.toString();
    }
}
